package com.woniuxy.gateway.fifter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.woniuxy.commons.bean.ResponseResult;
import com.woniuxy.commons.enums.ResponseStatus;
import com.woniuxy.commons.enums.TokenEnum;
import com.woniuxy.commons.util.JWTUtil;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;

/**
 * @author: 15489  <br/>
 * Date: 2022/3/29:14:16  <br/>
 * Description:
 */
@Component
public class AuthFilter implements GlobalFilter, Ordered {

    @Resource
    private RedisTemplate<String,Object> redisTemplate;

    // 执行过滤方法
    // 参数1：交换机
    // 参数2：过滤器链
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //1.得到url
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        String url = request.getPath().toString();

        //2.判断url是否需要认证
        if (requireAuth(url)){
            //3.需要认证，判断是否认证过
            //3.1.先获取token、refreshToken
            List<String> tokens = request.getHeaders().get("authorization");
            List<String> refreshTokens = request.getHeaders().get("refreshToken");
            System.out.println(tokens);
            //判断是否为null
            if (tokens.get(0).equals("null") || refreshTokens.get(0).equals("null")){
                // 需要去认证
                return goLogin(response);

            }

            //3.2.判断token、refreshToken（redis）是否合法
            //3.2.1.refreshToken
            if (!redisTemplate.hasKey(refreshTokens.get(0))){
                // 不包含：过期、伪造
                // 去登录
                System.out.println("refreshToken非法");
                return goLogin(response);
            }

            //3.2.2.校验token
            TokenEnum result = JWTUtil.verify(tokens.get(0));
            if (result.equals(TokenEnum.TOKEN_BAD)){
                // 非法
                System.out.println("token非法");
                return goLogin(response);
            }
            //
            if (result.equals(TokenEnum.TOKEN_EXPIRE)){
                // 过期
                System.out.println("token过期");
                // 重新生成token：redis中token更新、将token返回给浏览器
                // 从原来的token中获取用户账号，生成新的token
                String token = JWTUtil.generateToken(JWTUtil.getUname(tokens.get(0)));

                //更新redis中的token
                redisTemplate.opsForHash().put(refreshTokens.get(0),"token",token);

                //将新token放到响应头中返回
                response.getHeaders().add("authorization",token);
                //暴露头：为了能在axios的响应拦截中获取到头
                response.getHeaders().add("Access-Control-Expose-Headers","authorization,refreshToken");
            }
        }
        // 放行
        return chain.filter(exchange);
    }

    // 返回结果，要求登录
    private Mono<Void> goLogin(ServerHttpResponse response) {
        ResponseResult<Object> result = new ResponseResult<>();
        result.setCode(500);
        result.setMessage("请求登录");
        result.setStatus(ResponseStatus.NO_LOGIN);
        DataBuffer dataBuffer = null;
        //转换成JSON数据返回
        try {
            byte[] data = new ObjectMapper().writeValueAsString(result).getBytes();
            // 封装数据
            dataBuffer = response.bufferFactory().wrap(data);
            // 设置响应头
            response.getHeaders()
                    .add("Content-Type","application/json;charset=utf-8");
            // 返回数据
        }catch (Exception e){
            e.printStackTrace();
        }
        return response.writeWith(Mono.just(dataBuffer));
    }

    // 指定执行顺序
    @Override
    public int getOrder() {
        return 0;
    }

    // 判断url是否需要认证
    private boolean requireAuth(String url){
        // 需要、不需要认证的url都应该放到数据库中，不应该在程序中写死，维护不方便
        List<String> urls = Arrays.asList("/auth/login","/auth/regist");
        for(String str : urls){
            if (url.startsWith(str)){
                return false;
            }
        }
        return true;
    }
}

